package org.goldfish.demo.apicall.analysis.code.upload.codeselect;

import lombok.extern.slf4j.Slf4j;
import org.eclipse.jgit.ignore.FastIgnoreRule;
import org.eclipse.jgit.ignore.IgnoreNode;
import org.eclipse.jgit.ignore.IgnoreNode.MatchResult;
import org.springframework.stereotype.Component;

import java.io.ByteArrayInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.util.*;
import java.util.stream.Collectors;

@Slf4j
@Component
public class GitIgnoreScanner {

    /**
     * 缓存目录对应的 IgnoreNode，避免重复加载
     *
     */
    private final Map<File, IgnoreNode> ignoreNodeCache = new HashMap<>();

    public List<File> scanWithGitignore(File rootDir) throws IOException {
        List<File> result = new ArrayList<>();
        scanDir(rootDir, getIgnoreNode(rootDir), result);
        return result;
    }

    private void scanDir(File dir, IgnoreNode parentIgnoreNode, List<File> result) throws IOException {
        log.info("扫描目录{}", dir);
        if (dir.isDirectory() && dir.getName().equals(".git")) {
            return;  // 跳过.git目录
        }

        IgnoreNode ignoreNode = getIgnoreNode(dir);
        IgnoreNode combinedIgnoreNode = combineIgnoreNodes(parentIgnoreNode, ignoreNode);


        File[] children = dir.listFiles();
        if (children == null) return;

        for (File child : children) {
            String relativePath = dir.toPath().relativize(child.toPath()).toString();

            MatchResult match = combinedIgnoreNode != null
                    ? combinedIgnoreNode.isIgnored(relativePath, child.isDirectory())
                    : MatchResult.NOT_IGNORED;

            if (match == MatchResult.IGNORED) {
                // 被.gitignore忽略，跳过
                continue;
            }

            if (child.isDirectory()) {
                scanDir(child, combinedIgnoreNode, result);
            } else {
                result.add(child);
            }
        }
    }

    private IgnoreNode getIgnoreNode(File dir) throws IOException {
        if (ignoreNodeCache.containsKey(dir)) {
            return ignoreNodeCache.get(dir);
        }
        File gitignoreFile = new File(dir, ".gitignore");
        IgnoreNode node = null;
        if (gitignoreFile.exists()) {
            try (FileInputStream fis = new FileInputStream(gitignoreFile)) {
                node = new IgnoreNode();
                node.parse(fis);
            }
        }
        ignoreNodeCache.put(dir, node);
        return node;
    }

    private IgnoreNode combineIgnoreNodes(IgnoreNode parent, IgnoreNode current) {
        IgnoreNode combined = new IgnoreNode();

        try {
            if (parent != null) {
                String parentRules = parent.getRules().stream()
                        .map(Object::toString)
                        .collect(Collectors.joining("\n"));
                combined.parse(new ByteArrayInputStream(parentRules.getBytes(StandardCharsets.UTF_8)));
            }

            if (current != null) {
                String currentRules = current.getRules().stream()
                        .map(Object::toString)
                        .collect(Collectors.joining("\n"));
                combined.parse(new ByteArrayInputStream(currentRules.getBytes(StandardCharsets.UTF_8)));
            }
        } catch (IOException e) {
            throw new RuntimeException("Failed to combine ignore rules", e);
        }

        return combined;
    }



}
